/**
 * Update a cardlet list with animations
 */
function updateCardlets(data){
	var speed=1000;
	var targetedElement=$(this).targetedElement();
	var existingCardlets=targetedElement.children('.cardlet[object_id]');

	var sortFunction=getFunction($(this).closest('form').find('select[ui_role=order_by] option:selected').data('sort_function'));

	//Calculate the mergin between two cardlets:
	var merging=14;
	if(2<=existingCardlets.size()){
		merging=
		existingCardlets.slice(1,2).position().top
		-(existingCardlets.first().position().top+existingCardlets.first().height());
	}


	//Prepare a "set" of the new cardlets
	var newCardlets={};
	for(i in data.entries){
		newCardlets[data.entries[i].id]=i;
	}

	existingCardlets.prepareForAnimation();

	//Determine which cardlets should be preserved, which should be removed,
	//and which should be created:
	var cardletsToPreserve=[];
	var cardletsToRemove=[];
	existingCardlets.each(function(){
			var objectId=parseInt($(this).attr('object_id'));
			if(objectId in newCardlets){
				//Preserved cardlets should still be updated:
				var tmpCardlet=$(renderEjs(data.entries[newCardlets[objectId]]));
				$(this).htmlJQ(tmpCardlet.html());
				$(this).find('img[unloaded_src]').each(function(){
						$(this).attr('src',$(this).attr('unloaded_src'));
						$(this).removeAttr('unloaded_src');
				});
				$(this).data('model_object',data.entries[newCardlets[objectId]]);
				delete newCardlets[objectId];
				cardletsToPreserve.push(this);
			}else{
				cardletsToRemove.push(this);
			}
	});

	//Animate the cardlets to remove:
	$(cardletsToRemove).each(function(index){
			var cardlet=$(this);
			setTimeout(function(){
					cardlet.animate(
						{top:cardlet.position().top+$(window).height(),left:$(window).width()-cardlet.width()/10},{
							duration:speed,
							specialEasing:{
								top:'easeInQuart',
								left:'easeOutQuart'
							},
							complete:function(){
								$(this).remove();
							}
					});
			},Math.min(5,index)*50);
	});


	//Get the correct order
	var newCardletsOrder=$.map(newCardlets,function(a){return a;}).sort();
	if(sortFunction){
		newCardletsOrder=newCardletsOrder.sort(function(a,b){
				return sortFunction(data.entries[a],data.entries[b]);
		});
	}
	//Animate the cardlets to create:
	var topSoFar=targetedElement.position().top;
	var previousCardlet=null;
	$.each(newCardletsOrder,function(index){
			var entry=data.entries[index];
			var cardlet=$(renderEjs(entry));
			cardlet.attr('object_id',entry.id);
			if(previousCardlet){
				previousCardlet.after(cardlet);
			}else{
				targetedElement.prepend(cardlet);
			}
			previousCardlet=cardlet;
			applyJQueryTo(cardlet);
			cardlet.data('model_object',entry);
			var targetTopForThisCardlet=topSoFar;
			var targetLeft=cardlet.position().left;
			topSoFar=topSoFar+cardlet.height()+merging;
			cardlet.css('top',targetTopForThisCardlet+$(window).height());
			cardlet.css('left',-cardlet.width()*9/10);
			cardlet.css('position','absolute');
			setTimeout(function(){
					cardlet.animate(
						{top:targetTopForThisCardlet,left:targetLeft},{
							duration:speed,
							specialEasing:{
								top:'easeOutQuart',
								left:'easeInQuart'
							}
					});
			},Math.min(5,index)*50);
	});

	//Animate the cardlets to preserve:
	for(var i in cardletsToPreserve){
		var cardlet=$(cardletsToPreserve[i]);
		var targetTopForThisCardlet=topSoFar;
		topSoFar=topSoFar+cardlet.height()+merging;
		var distance=Math.abs(cardlet.position().top-targetTopForThisCardlet);
		cardlet.animate({top:targetTopForThisCardlet},speed*(distance/$(window).height()));
	}

	//After the animation is done, remove the absolute positioning off the cardlets.
	targetedElement.children('.cardlet').finishAnimationWhenDone();
}

/**
 * Set an element's css properties so it could be animated.
 */
$.fn.prepareForAnimation=function(){
	$(this).each(function(){
			var elem=$(this);
			elem.css('top',elem.position().top);
			elem.css('left',elem.position().left);
	});
	$(this).css('position','absolute');
};

/**
 * Set an element's css properties back after the animation.
 */
$.fn.finishAnimation=function(){
	$(this).css('position','');
	$(this).css('top','');
	$(this).css('left','');
};

/**
 * Check when the element's(or elements') animation is done, and when it's done
 * call `finishAnimation` on it.
 */
$.fn.finishAnimationWhenDone=function(){
	var target=$(this);
	var somethingWasAnimated=false;
	(function(){
			if(target.is(':animated')){
				somethingWasAnimated=true;
				setTimeout(arguments.callee,250);
			}else if(somethingWasAnimated){
				target.finishAnimation();
				showImagesThatAreNowInView();
				if(FB){
					setTimeout(fixCanvasHeight,100);
				}
			}else{
				setTimeout(arguments.callee,250);
			}
	})();
};

/**
 * Easing motion that makes the element return to the same place when done.
 */
$.easing.returnToSamePlace=function(animationPercentage,startValue,endValue){
	return $.easing.easeOutQuart(
		animationPercentage<0.5 ? 2*animationPercentage : 2*(1-animationPercentage),
		startValue,endValue);
}

registerJQueryFunction(function(selector){
		/**
		 * Make the select element that controls ordering.
		 */
		selector('select[ui_role=order_by]').change(function(){
				var sortFunction=getFunction($(this).find('option:selected').data('sort_function'));
				var targetedElement=$(this).targetedElement();

				var cardlets=targetedElement.find('div.cardlet');
				cardlets.prepareForAnimation();

				var sortedCardlets=$.map(cardlets,$).sort(function(a,b){
						return sortFunction(a.data('model_object'),b.data('model_object'));
				});

				//Determine where each element in the list should go:
				var newToOldIndexMap={};
				$.each(sortedCardlets,function(index){
						newToOldIndexMap[this.index()]=index;
				});

				var merging=14;
				if(2<=cardlets.size()){
					merging=
					cardlets.slice(1,2).position().top
					-(cardlets.first().position().top+cardlets.first().height());
				}

				var topSoFar=targetedElement.position().top;
				$.each(sortedCardlets,function(index){
						var cardlet=this;
						targetedElement.append(cardlet.detach());
						var targetTopForThisCardlet=topSoFar;
						topSoFar=topSoFar+cardlet.height()+merging;

						//Choose the horizontal path's magnitude and direction:
						var leftPathCurve=cardlet.position().left+200/sortedCardlets.length*(newToOldIndexMap[index]-index);

						cardlet.animate(
							{
								top:targetTopForThisCardlet,
								left:leftPathCurve
							},{
								duration:1000,
								specialEasing:{
									top:'easeInOutQuart',
									left:'returnToSamePlace'
								}
						});
				});

				cardlets.finishAnimationWhenDone();
		});
});
